// LineExtractor.java
// Copyright (c) 2010 William Whitney
// All rights reserved.
// This software is released under the BSD license.
// Please see the accompanying LICENSE.txt for details.
package com.starsoft.verifycode.split;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;

/**
 * Saves all the characters in an image to an output directory individually.
 * 
 * @author William Whitney
 */
public class LineSplit {

	protected int whiteThreshold = 128;
	protected int std_width = 14;
	protected int std_height = 16;
	
	public boolean isWhite(int colorInt) {
		final Color color = new Color(colorInt);
		if (color.getRed() + color.getGreen() + color.getBlue() > whiteThreshold*3) {
			return true;
		}
		return false;
	}
	
	public List<BufferedImage> split(BufferedImage image) {
		List<BufferedImage> resultImages = new ArrayList<BufferedImage>();
		
		List<BufferedImage> verticalImages = verticalSplit(image);
		for(BufferedImage sliceimage: verticalImages){
			resultImages.addAll(horizontalSplit(sliceimage));
		}
		
		for(int i=0;i<resultImages.size();i++){
			BufferedImage resizeImg = resize(resultImages.get(i));
			resultImages.set(i, resizeImg);
		}
		return resultImages;
	}

	public List<BufferedImage> verticalSplit(BufferedImage image){
		final List<BufferedImage> subImgs = new ArrayList<BufferedImage>();
		final int width = image.getWidth();
		final int height = image.getHeight();
		
		final List<Integer> splitPoints = new ArrayList<Integer>();

		boolean targetWhite = false;
		for (int i = 0; i < height; ++i) {
			boolean isWhiteLine = true;
			for (int j = 0; j < width; ++j) {
				if (!isWhite(image.getRGB(j, i))) {
					isWhiteLine = false;
					break;
				}
			}
			if (!isWhiteLine && !targetWhite) {
				splitPoints.add(i);
				targetWhite = true;
			} else if (isWhiteLine && targetWhite) {
				splitPoints.add(i);
				targetWhite = false;
			}
		}
		if(splitPoints.size()%2!=0){
			splitPoints.add(height);
		}

		for (int i = 0; i < splitPoints.size()/2; i++) {
			int length = splitPoints.get(i*2+1) - splitPoints.get(i*2);
			if (length > 5) {
				subImgs.add(image.getSubimage(0, splitPoints.get(i*2), width, length));
			}
		}
		return subImgs;
	}
	
	public List<BufferedImage> horizontalSplit(BufferedImage image){
		final List<BufferedImage> subImgs = new ArrayList<BufferedImage>();
		final int width = image.getWidth();
		final int height = image.getHeight();
		final List<Integer> splitPoints = new ArrayList<Integer>();

		boolean targetWhite = false;
		for (int i = 0; i < width; ++i) {
			boolean isWhiteLine = true;
			for (int j = 0; j < height; ++j) {
				if (!isWhite(image.getRGB(i, j))) {
					isWhiteLine = false;
					break;
				}
			}
			if (!isWhiteLine && !targetWhite) {
				splitPoints.add(i);
				targetWhite = true;
			} else if (isWhiteLine && targetWhite) {
				splitPoints.add(i);
				targetWhite = false;
			}
		}
		if(splitPoints.size()%2!=0){
			splitPoints.add(height);
		}

		for (int i = 0; i < splitPoints.size()/2; i++) {
			int length = splitPoints.get(i*2+1) - splitPoints.get(i*2);
			if (length > 5) {
				BufferedImage subImg = image.getSubimage(splitPoints.get(i*2), 0, length, height);
				subImg = removeBlank(subImg);				
				subImgs.add(subImg);
			}
		}
		return subImgs;
	}
	
	private BufferedImage removeBlank(BufferedImage image) {
		int width = image.getWidth();
		int height = image.getHeight();

		int topPoint = 0, bottomPoint = height;
		Label1: for (int i = 0; i < height / 2; ++i) {
			for (int j = 0; j < width; ++j) {
				if (!isWhite(image.getRGB(j, i))) {
					topPoint = i;
					break Label1;
				}
			}
		}
		Label2: for (int i = height - 1; i > height / 2; --i) {
			for (int j = 0; j < width; ++j) {
				if (!isWhite(image.getRGB(j, i))) {
					bottomPoint = i + 1;
					break Label2;
				}
			}
		}

		return image.getSubimage(0, topPoint, width, bottomPoint - topPoint);
	}
	
	public BufferedImage resize(BufferedImage image) {
		// Scale image so that both the height and width are less than std size
		if (image.getWidth() > std_width) {
			// Make image always std_width wide
			double scaleAmount = (double) std_width / (double) image.getWidth();
			AffineTransform tx = new AffineTransform();
			tx.scale(scaleAmount, scaleAmount);
			AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);
			image = op.filter(image, null);
		}

		if (image.getHeight() > std_height) {
			// Make image always std_height tall
			double scaleAmount = (double) std_height / (double) image.getHeight();
			AffineTransform tx = new AffineTransform();
			tx.scale(scaleAmount, scaleAmount);
			AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);
			image = op.filter(image, null);
		}

		// Paint the scaled image on a white background
		BufferedImage normalizedImage = new BufferedImage(std_width, std_height, BufferedImage.TYPE_INT_RGB);
		Graphics2D g = normalizedImage.createGraphics();
		g.setColor(Color.WHITE);
		g.fillRect(0, 0, std_width, std_height);

		// Center scaled image on new canvas
		int x_offset = (std_width - image.getWidth()) / 2;
		int y_offset = (std_height - image.getHeight()) / 2;

		g.drawImage(image, x_offset, y_offset, null);
		g.dispose();

		return normalizedImage;
	}
}
